訪問案例網站時默認調用index.jsp,在此頁面設定轉向MainSvl。
http://localhost:8080/BookShop
新建index.jsp
<%request.getRequestDispatcher("/MainSvl").forward(request, response);%>
新建MainSvl(控制層)
@WebServlet("/MainSvl")
public class MainSvl extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
BookBiz biz = new BookBiz();
try {
List<TBook> books = biz.getAllBooks();
request.setAttribute("books", books);
request.getRequestDispatcher("/WEB-INF/main/main.jsp").forward(request, response);
}catch(Exception e){
request.setAttribute("msg", "網路異常,請跟網站管理員聯繫。");
request.getRequestDispatcher("/error.jsp").forward(request, response);
}
}
(餘略)
}
新建BookBiz(服務層)
public class BookBiz {
public List<TBook> getAllBooks() throws Exception{
IBookDao dao = new BookDaoMysql();
try {
return dao.getAllBooks();
}finally{
dao.closeConnection();
}
}
}
新建BookDaoMysql(持久層:負責DB訪問)
public class BookDaoMysql extends BaseDao{
public List<TBook> getAllBooks() throws Exception{
List<TBook> books = null;
String sql = "select isbn,name,press,price,pdate from tbook order by isbn";
//從BaseDao取得connection = DriverManager.getConnection(dbInfo.getUrl(), dbInfo.getUname(), dbInfo.getPwd());
this.openConnection();
PreparedStatement ps = this.connection.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
if(rs != null) {
books = new ArrayList<TBook>();
//當rs裡面還有下一件資料時
while(rs.next()) {
//把取出的資料都保存到TBook物件中
TBook bk = new TBook();
bk.setBname(rs.getString("bname"));
bk.setIsbn(rs.getString("isbn"));
bk.setPdate(rs.getString("Pdate"));
bk.setPress(rs.getString("press"));
bk.setPrice(rs.getString("price"));
//將TBook物件加入列表
books.add(bk);
}
}
return books;
使用JSTL的<c:forEath>標籤即可簡單取出列表
var為保存資料用的變數名(可自取),items為資料來源列表的名字。
<table border="1" width=100%>
<c:forEach var="bk" items="${books }">
<tr>
<td rowspan=3>
<img width=100 height=100 src="<%=basePath%>BookPicSvl?isbn=${bk.isbn }">
</td>
<td colspan=2 align=center style="color:red">
<a href="<%=basePath%>BookDetailSvl?isbn=${bk.isbn }">${bk.bname }</a>
</td>
</tr>
<tr><td>商品價格</td><td>${bk.price }</td></tr>
<tr><td>出版社</td><td>${bk.press }</td></tr>
</c:forEach>
</table>
在這個案例中,使用了標籤和<%=basePath%>,這兩個部分是常用的代碼,可以抽取出來放在base.jsp頁面中。
使用以下指令進行靜態引用
<%@include file ="/WEB-INF/base.jsp" %>
另外一種引用方法為動態引用,不過動態引用就無法取得base頁面裡的變數。得要在頁面靜態編譯時便建立好變數才能引用。
<jsp:include page="/WEB-INF/base.jsp"></jsp:include>
c標籤也有引用頁面的import方法
<c:import url="base.jsp"></c:import>
不過這樣做的話就必須先加載以下標籤庫
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
在本案例中標籤庫的加載交給base頁面做了,所以選擇include file指令更方便。
當用戶進行下載圖片等比較耗時的處理,可以使用NIO(非阻塞流)技術(一種異步處理技術)。
新建BookPicSvl並開啟支援異步模式。
@WebServlet(urlPatterns="/BookPicSvl",asyncSupported=true)
public class BookPicSvl extends HttpServlet {
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
String isbn = request.getParameter("isbn");
if(isbn == null) {
throw new RuntimeException("isbn不能為空");
}
BookBiz biz = new BookBiz();
try {
byte[] pic = biz.getBookPic(isbn);
if(pic != null) {
AsyncContext acontext = request.startAsync();
ServletOutputStream out = acontext.getResponse().getOutputStream();
out.setWriteListener(new MyPicWriter(out,acontext,pic));
}
}catch(Exception e){
Log.logger.error(e.getMessage(), e);
request.setAttribute("msg", "網路異常,請檢查。");
request.getRequestDispatcher("/error.jsp").forward(request, response);
}
}
(餘略)
}
新建MyPicWriter
public class MyPicWriter implements WriteListener {
private ServletOutputStream out;
private AsyncContext ac;
private byte[] pic;
public MyPicWriter(ServletOutputStream out,AsyncContext ac,byte[] pic) {
this.ac = ac;
this.out = out;
this.pic = pic;
}
@Override
public void onError(Throwable t) {
t.printStackTrace();
}
@Override
public void onWritePossible() throws IOException {
try {
if(pic != null && out.isReady()) {
out.write(pic);
//out.flush(); --此處不能使用flush
out.close();
}
}catch(Exception e){
e.printStackTrace();
}finally {
ac.complete();
}
}
}
當圖片資料不為空,且輸出流已經準備好時,使用write(pic)將資料寫出後關閉輸出流。最後關閉異步處理物件。
服務層的動作
public class BookBiz {
public byte[] getBookPic(String isbn) throws Exception{
IBookDao dao = new BookDaoMysql();
try {
return dao.getBookPic(isbn);
}finally{
dao.closeConnection();
}
}
}
DAO層的動作
public class BookDaoMysql extends BaseDao{
public byte[] getBookPic(String isbn) throws Exception{
byte[] pic = null;
String sql = "select pic from tbook where isbn = ?";
this.openConnection();
PreparedStatement ps = this.connection.prepareStatement(sql);
ps.setString(1, isbn);
ResultSet rs = ps.executeQuery();
if(rs != null) {
while(rs.next()) {
pic = rs.getBytes("pic");
break;
}
}
rs.close();
ps.close();
return pic;
}
}
利用特定條件取得資料時,SQL文內會放置"?"佔位符。跟取得全部列表時不一樣。
當SQL文內有佔位符時,依照其數量,使用PreparedStatement物件的setString(index, value)將值代入;
例如ps.setString(1, isbn);,意思是將isbn變數內的值代入第一個佔位符內。
使用while迴圈來確保至少有一行資料,然後將其讀取。
疑問:之前的教材中沒有關閉rs和ps,不知道為什麼。
在main.jsp和BookDetail.jsp中,使用標籤來請求圖片的數據流。
<img src="<%=basePath%>BookPicSvl?isbn=${bk.isbn}"/>
在首頁列表中點擊任一商品之後,跳轉到商品細節頁面。連結長得像下面這樣
http://localhost:8080/BookShop/BookDetailSvl?isbn=is001
新建BookDetailSvl
@WebServlet("/BookDetailSvl")
public class BookDetailSvl extends HttpServlet {
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String isbn = request.getParameter("isbn");
if(isbn == null) {
throw new RuntimeException("isbn不能為空");
}
BookBiz biz = new BookBiz();
try {
TBook book = biz.getBookDetail(isbn);
request.setAttribute("book", book);
request.getRequestDispatcher("/WEB-INF/main/BookDetail.jsp").forward(request, response);
}catch(Exception e){
Log.logger.error(e.getMessage(),e);
request.setAttribute("msg", "網路異常,請跟網站管理員聯繫。");
request.getRequestDispatcher("/error.jsp").forward(request, response);
}
}
}
服務層
新增getBookDetail(isbn)方法
public TBook getBookDetail(String isbn) throws Exception{
IBookDao dao = new BookDaoMysql();
try {
return dao.getBookDetail(isbn);
}finally{
dao.closeConnection();
}
}
DAO層
新增getBookDetail(String isbn)方法
public TBook getBookDetail(String isbn) throws Exception{
TBook book = null;
String sql = "select isbn,bname,press,price,pdate from tbook where isbn = ?";
this.openConnection();
PreparedStatement ps = this.connection.prepareStatement(sql);
ps.setString(1, isbn);
ResultSet rs = ps.executeQuery();
if(rs != null) {
while(rs.next()) {
book.setBname(rs.getString("bname"));
book.setIsbn(rs.getString("isbn"));
book.setPdate(rs.getString("Pdate"));
book.setPress(rs.getString("press"));
book.setPrice(rs.getString("price"));
break;
}
}
return book;
}
BookDetail.jsp頁面
(前略)
<table border="1" width=100%>
<tr>
<td rowspan=3>
<img width=100 height=100 src="<%=basePath%>BookPicSvl?isbn=${book.isbn }">
</td>
<td colspan=2 align=center style="color:red">
${book.bname }
</td>
</tr>
<tr><td>商品價格</td><td>${book.price }</td></tr>
<tr><td>出版社</td><td>${book.press }</td></tr>
<tr><td height=300 colspan=3>圖書簡介</td></tr>
<tr><td colspan=3 align="center">
<a href="<%=basePath%>user/ShopCarAddSvl?isbn=${book.isbn }">加入購物車</a>
<a href="<%=basePath%>MainSvl">返回</a>
</td>
</tr>
</table>